/**
 * 
 */
package com.workable.assignment.utilities;

import java.lang.reflect.Field;
import java.util.Stack;

/**
 * @author npapadopoulos
 * 
 */
public class StringUtil {

    private static final String PACKAGE_PREFIX = "com.workable.test";

    public static String toString(Object... objects) {
        StringBuilder sb = new StringBuilder();
        Stack<Class<?>> stack = new Stack<Class<?>>();
        for (int i = 0; objects != null && i < objects.length && objects[i] != null; i++) {
            Object o = objects[i];
            Class<?> type = o.getClass();
            while (type != Object.class && type.getSuperclass() != Object.class
                && type.getPackage().getName().startsWith(PACKAGE_PREFIX)) {
                stack.add(type.getSuperclass());
                type = type.getSuperclass();
            }
            for (Class<?> c : stack) {
                String s = toString(c, o);
                if (s != null && !s.isEmpty()) {
                    sb.append(s).append(", ");
                }
            }
            sb.append(toString(o.getClass(), o));
            if (i + 1 < objects.length) {
                sb.append(", ");
            }
        }
        return sb.toString();
    }

    /**
     * Method that creates output from all the members of a class.
     * 
     * @param c Class type of object from which we're pulling data
     * @param o Object
     */
    private static String toString(Class<?> c, Object o) {
        StringBuilder sb = new StringBuilder();
        if (o instanceof Enum) {
            sb.append(o);
        } else if (!c.getPackage().getName().startsWith(PACKAGE_PREFIX)) {
            sb.append(o);
        } else {
            boolean hasFields = false;
            boolean addDelim = false;
            for (Field f : c.getDeclaredFields()) {
                if (!(java.lang.reflect.Modifier.isStatic(f.getModifiers()))) {
                    if (!hasFields) {
                        sb.append(c.getSimpleName()).append(":[");
                        hasFields = true;
                    }
                    try {
                        f.setAccessible(true);
                        Object value = f.get(o);
                        if (addDelim) {
                            sb.append(", ");
                        } else {
                            addDelim = true;
                        }
                        sb.append(f.getName()).append("=");

                        if (value == null) {
                            sb.append("null");
                        } else {
                            sb.append(toString(value.getClass(), value));
                        }
                    } catch (Exception e) {
                        sb.append(e.toString());
                    }
                }
            }
            if (hasFields) {
                sb.append("]");
            }
        }
        return sb.toString();
    }
}
